import math
from math import *

import pyglet
from pyglet.window import key, mouse
from pyglet.gl import *
from pyglet import gl

import cocos
from cocos.actions import *
from cocos.layer import *
from cocos.scene import *
from cocos.sprite import *
from cocos.director import director
from cocos.scenes.transitions import *
from cocos.euclid import Point2
from cocos.draw import *
from cocos.text import *

import Box2D as box2d

from globalvars import *
import ui
import listener
import map
from res import Images

###--------------------------###       
#        
#    CameraLayer
#	- viewing the GameWorld done with this.
#
###---------------------------### 


class CameraLayer(Layer):        
    """
    Asumes no rotation is performed
    """
    def __init__(self):
        super(Layer,self).__init__()
        self.anchor = (0,0)
        self.lastAnchor = Point2(0,0)
        
    def screen_to_world(self, sx, sy):
        wx = (sx - self.anchor_x) / self.scale
        wy = (sy - self.anchor_y) / self.scale
        return wx, wy

    def world_to_screen(self, wx, wy):
        sx = wx * self.scale + self.anchor_x
        sy = wy * self.scale + self.anchor_y
        return sx, sy

    def map_world_to_sc( self, wx, wy, sx, sy ):
        """ set up the camera so that point wx,wy in world coords shows at coords sx,sy in screen"""
        # we want sx,sy = world_to_screen(sx,sy), working in the equation
        self.anchor_x = sx-wx*self.scale
        self.anchor_y = sy-wy*self.scale
        
    def point_to(self, wx, wy):
        """ the screen center will show the world at point (wx, wy)"""
        w, h = director.get_window_size()
        wx, wy = director.get_virtual_coordinates(wx, wy)
        # we want the (world_x, world_y) be transformed to (w/2, h/2)
        self.lastAnchor = Point2(self.anchor_x, self.anchor_y)
        self.map_world_to_sc(wx,wy,w/2,h/2)
        

###--------------------------###       
#        
#    GameWorld
#	- The happenings
#
###---------------------------### 

class GameWorld(Layer, box2d.b2World):
    """
    GameWorld handles 
    - Entities
    - Rendering with cocos.layer.ColorLayer
    - Physics world with box2d.b2World,
    - Physics contacts with GameContactListener and GameContactPoint
    """
    is_event_handler = True
    
    DT = 1.0/60.0
    VEL_ITERS = 10
    POS_ITERS = 8
    DRAG_SPEED = 2.0
    
    ZOOM_STEP = 0.04
    DEFAULT_ZOOM = 0.7#0.3
    viewZoom = None
    isPaused = None
    def __init__(self, cameraLayer=None):
        "Cocos2d and gameplay related"
        Layer.__init__(self)
        self.sprites = self.children # merely a pointer
        self.isPaused = False
        
        "entity"
        self.entities = {}
        
        "box2d world init"
        worldAABB=box2d.b2AABB()
        worldAABB.lowerBound = (-500, -500.0)
        worldAABB.upperBound = ( 500.0, 500.0)
        box2d.b2World.__init__(self, worldAABB, (0,0), True)
        self.gravity = (0,0)
        
        "The listeners"
        self.points = []
        self.contactListener = listener.GameContactListener()
        self.contactListener.test = self
        self.SetContactListener(self.contactListener)
        #self.debugDraw = listener.GameDebugDraw()
        #self.debugDraw.surface = director.window.screen
        #self.SetDebugDraw(self.debugDraw)
        
        "The map"
        self.map = map.Map1(self)
        self.add(self.map)
        self.turtle = self.map.turtle

        "set up the view"
        self.cameraLayer = cameraLayer
        w, h = director.get_window_size()
        self.world_width = world_width = 2*w
        self.world_height = world_height = 2*h
        self.pointedView = (self.turtle.body.position*RATIO).tuple()
        self.cameraLayer.scale = self.DEFAULT_ZOOM
        self.cameraLayer.point_to(*self.pointedView)
        
    """
    Update methods
    """
        
    def on_update(self,dt):
        "Update sprites, contact listener, world"
        self.parent.parent.gameHUD.update(dt)
        for entity in self.entities.values():
            entity.update(dt)
        self.updatePhysics(dt)
        self.updateView()
    
    def updatePhysics(self,dt):
        self.points = []
        self.Step(self.DT, self.VEL_ITERS, self.POS_ITERS)
        self.Validate()
    
    def updateView(self):
        "Update view"
        if self.cameraLayer:
            cl = self.cameraLayer
            cl.point_to(*self.pointedView)
            #if Point2(cl.anchor_x, cl.anchor_y) != cl.lastAnchor: pass
    
    """
    Entity management methods
    """
    
    def addEntity(self,newEntity,*args,**kwargs):
        "Add an entity"
        self.entities[newEntity.id] = newEntity
        self.add(newEntity,*args,**kwargs)
        
    def removeEntity(self,idOrEntity):
        "Remove entity given itself or its ID"
        if type(idOrEntity) == int:
            ent = self.getEntityByID(idOrEntity)
            self.remove(ent)
            del self.entities[idOrEntity]
            #del ent
        else:
            id = self.getIDByEntity(idOrEntity)
            self.remove(idOrEntity)
            del self.entities[id]
            #del idOrEntity
        
    def getEntityByID(self,id):
        return self.entities[idOrEntity]
        
    def getIDByEntity(self,entity):
        return ([k for k, v in self.entities.iteritems() if v == entity][0])
    
    """
    Window events methods
    """
    def on_mouse_press(self,x,y,buttons,modifiers):
        #~ hitX, hitY =self.cameraLayer.screen_to_world(x,y)
        #~ self.mPos = Point2(hitX,hitY)
        #~ self.mPressed = True
        if buttons & mouse.LEFT:
            hit_x, hit_y = self.cameraLayer.screen_to_world(x,y)
            self.turtle.on_mouse_press(hit_x, hit_y, buttons, modifiers)
    
    def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
        if buttons & mouse.LEFT:
            hit_x, hit_y = self.cameraLayer.screen_to_world(x,y)
            self.turtle.on_mouse_drag(hit_x, hit_y, dx, dy,buttons, modifiers)
        if buttons & mouse.RIGHT:
            self.pointedView += Point2(-dx,-dy)*self.DRAG_SPEED
    
    def on_mouse_released(self,x,y,buttons,modifiers):
        pass
    
    def on_key_press(self, symbol, modifiers):
        "Open pause menu"
        self.turtle.on_key_press(symbol,modifiers)
        if symbol == key.ESCAPE:
            self.parent.parent.on_pause()
            return True

    def on_mouse_scroll(self, x, y, dx, dy):
        self.cameraLayer.scale += dy*self.ZOOM_STEP
        #director.window.dispatch_event('on_mouse_press')
       
    def on_activate(self):
        "continue update if window is active"
        pyglet.clock.unschedule(self.on_update)
        pyglet.clock.schedule_interval(self.on_update, self.DT)
        
    def on_deactivate(self):
        "pause update if window is inactive"
        pyglet.clock.unschedule(self.on_update)

    
###--------------------------###       
#        
#    GameHUD
#
###---------------------------### 

class GameHUD(Layer):
    "Status report- headsup display"
    def __init__(self):
        "Init"
        Layer.__init__(self)
        self.winSize = director.window.get_size()
        #self.scale = 1
        #self.offset = box2d.b2Vec2(-260,-160)
        self.turtle = None
  
        "meters"
        self.meters = []
        self.inkMeter = self.InkMeter()
        self.inkMeter.position = (40,270)
        self.meters.append(self.inkMeter)
        
    def setPlayerTurtle(self,turtle=None):
        self.turtle = turtle
        self.inkMeter.setObjectAndAttr(self.turtle.turtlePen, "inkLeft")
        for m in self.meters:
            self.add(m)
        
    def update(self,dt):
        for m in self.meters:
            m.update(dt)


    ###--------------------------###               
    #    BaseMeter
    ###--------------------------###      
        
    class BaseMeter(Canvas):
        def __init__(self, ratio=1, 
                     text="Unnamed Bar", textColor = (170,170,170,255), textSize=12, 
                     fgColor=(50,200,130,150), fgWidth=30, 
                     bgColor=(100,255,160,160), bgWidth = 20 ):
            "The HP Meter"
            Canvas.__init__(self)
            
            "Start and End"
            self.start = (0,0)
            self.end = (0,0)
            self.ratio = ratio
            self.object = None
            self.objectAttr = ""
            self.value = 0
            self.endValue = 0
            
            "Colors and width"
            self.fgColor = fgColor
            self.fgWidth = fgWidth
            self.bgColor = bgColor
            self.bgWidth = bgWidth            

            "Label"
            self.label = Label(text,anchor_x="left", anchor_y="center",
                               position =(0,0), font_size = textSize, color = textColor ) 
            self.add(self.label)
            
        def setObjectAndAttr(self,obj,attr):
            "To be implemented - set object ref and its attr"
    
        def updateValue(self):
            "To be implemented - update the value based on self.object and attr"
            pass
                
            
        def update(self,dt):
            "Updates"
            self.updateValue()
            if self.value != self.end[0]:
                self.end = (self.value,0)
                self._dirty = True
                self.draw()
            
        def render(self):
            "Render"
            self.set_color(self.bgColor)
            self.set_stroke_width(self.bgWidth)
            self.move_to(self.start)
            self.line_to((self.endValue, self.end[1]))
            
            self.set_color(self.fgColor)
            self.set_stroke_width(self.fgWidth)
            self.move_to(self.start)
            self.line_to(self.end)
    
    ###--------------------------###               
    #    InkMeter
    ###--------------------------###
            
    class InkMeter(BaseMeter):
        def __init__(self, ratio=0.6, text="", textColor = (255,255,255,255), textSize=14,
                    fgColor=(50,50,50,200), fgWidth=20, 
                     bgColor=(100,100,100,100), bgWidth = 22 ):
            GameHUD.BaseMeter.__init__(self, ratio, text, textColor, textSize, fgColor, fgWidth, bgColor, bgWidth )
            self.penUpColor = fgColor
            self.penDownColor = bgColor
            
        def setObjectAndAttr(self,obj,attr):
            "Ref-ed value"
            self.object = obj
            self.objectAttr = attr
            if self.object:
                exec("self.endValue = self.object."+self.objectAttr+ "*self.ratio")
    
    
        def updateValue(self):
            if self.object:
                exec("self.value = self.object."+self.objectAttr+ "*self.ratio")
                if self.object.turtle.penDown:
                    if self.label.element.text != "PenDown":
                        self.label.element.text = "PenDown"
                        self.fgColor = self.penUpColor
                        self._dirty = True
                        self.draw()
                elif not self.object.turtle.penDown:
                    if self.label.element.text != "PenUp":
                        self.label.element.text = "PenUp"
                        self.fgColor = self.penDownColor
                        self._dirty = True
                        self.draw()

        
###--------------------------###       
#        
#    GameScene
#
###---------------------------### 

class GameScene(Scene):
    "Game Scene"
    
    def __init__(self):
        Scene.__init__(self)
        self.menuOverlay = None

        self.cameraLayer = CameraLayer()
        self.gameWorld = GameWorld(self.cameraLayer)
        self.gameHUD = GameHUD()

        self.gameHUD.setPlayerTurtle(self.gameWorld.turtle)

        self.add(self.gameHUD, z=3)
        self.add(self.cameraLayer, z=2)
        self.cameraLayer.add(self.gameWorld)
		
        self.schedule_interval(self.gameWorld.on_update, self.gameWorld.DT)
        
    def on_pause(self):
        self.gameWorld.isPaused = True
        pyglet.clock.unschedule(self.gameWorld.on_update)
        self.menuOverlay = ui.MenuOverlay(self.gameWorld.isPaused)
        self.add(self.menuOverlay,z=4)
        self.menuOverlay.bg.image.opacity = 0
        self.menuOverlay.bg.image.do(FadeIn(0.2))
        
    def on_resume(self):
        self.gameWorld.isPaused = False
        pyglet.clock.schedule_interval(self.gameWorld.on_update, self.gameWorld.DT)
        self.remove(self.menuOverlay)
        self.menuOverlay = None
        
    def on_end_game(self):
        self.remove(self.menuOverlay)
        self.menuOverlay = None
        director.set_projection()
        director.replace(ZoomTransition(ui.MenuScene(), duration=0.3))
        
    


